package com.onesports.intelligent.k12.polarlight.service.index;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.onesports.framework.kit.common.util.ParamUtils;
import com.onesports.framework.kit.common.util.TimeUtils;
import com.onesports.intelligent.k12.polarlight.common.domain.entity.BaseEntity;
import com.onesports.intelligent.k12.polarlight.domain.entity.course.ClassInfo;
import com.onesports.intelligent.k12.polarlight.domain.entity.course.ClassStudent;
import com.onesports.intelligent.k12.polarlight.domain.entity.course.CourseScheduleDetail;
import com.onesports.intelligent.k12.polarlight.domain.entity.course.SpellingClassPeriod;
import com.onesports.intelligent.k12.polarlight.domain.entity.market.FundsTradeBill;
import com.onesports.intelligent.k12.polarlight.domain.entity.market.Orders;
import com.onesports.intelligent.k12.polarlight.domain.entity.organization.Coach;
import com.onesports.intelligent.k12.polarlight.domain.entity.organization.Organization;
import com.onesports.intelligent.k12.polarlight.domain.entity.organization.OrganizationCampus;
import com.onesports.intelligent.k12.polarlight.domain.entity.report.OperationHomePage;
import com.onesports.intelligent.k12.polarlight.domain.entity.report.OrganizationHomePage;
import com.onesports.intelligent.k12.polarlight.domain.entity.student.StudentAttendance;
import com.onesports.intelligent.k12.polarlight.enums.*;
import com.onesports.intelligent.k12.polarlight.mapper.course.ClassInfoMapper;
import com.onesports.intelligent.k12.polarlight.mapper.course.ClassStudentMapper;
import com.onesports.intelligent.k12.polarlight.mapper.course.CourseScheduleDetailMapper;
import com.onesports.intelligent.k12.polarlight.mapper.course.SpellingClassPeriodMapper;
import com.onesports.intelligent.k12.polarlight.mapper.market.FundsTradeBillMapper;
import com.onesports.intelligent.k12.polarlight.mapper.market.OrdersMapper;
import com.onesports.intelligent.k12.polarlight.mapper.organization.CoachMapper;
import com.onesports.intelligent.k12.polarlight.mapper.organization.OrganizationCampusMapper;
import com.onesports.intelligent.k12.polarlight.mapper.organization.OrganizationMapper;
import com.onesports.intelligent.k12.polarlight.mapper.report.OperationHomePageMapper;
import com.onesports.intelligent.k12.polarlight.mapper.report.OrganizationHomePageMapper;
import com.onesports.intelligent.k12.polarlight.mapper.student.StudentAttendanceMapper;
import lombok.RequiredArgsConstructor;
import org.aspectj.weaver.ast.Or;
import org.hibernate.criterion.Order;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Wsy
 **/
@Service
@RequiredArgsConstructor
public class HomePageService {

    private final OrganizationMapper organizationMapper;
    private final OrganizationCampusMapper organizationCampusMapper;
    private final CoachMapper coachMapper;
    private final StudentAttendanceMapper studentAttendanceMapper;
    private final ClassStudentMapper classStudentMapper;
    private final ClassInfoMapper classInfoMapper;
    private final SpellingClassPeriodMapper spellingClassPeriodMapper;
    private final CourseScheduleDetailMapper courseScheduleDetailMapper;
    private final OrdersMapper ordersMapper;
    private final FundsTradeBillMapper fundsTradeBillMapper;
    private final OperationHomePageMapper operationHomePageMapper;
    private final OrganizationHomePageMapper organizationHomePageMapper;

    /**
     * 保存首页数据
     *
     * @param date 当前时间
     */
    public void saveData(Date date) {
        //去除时分秒的当前日期
        Date currentDate = TimeUtils.toDate(TimeUtils.toText(date, TimeUtils.DATE_FORMAT), TimeUtils.DATE_FORMAT);
        //运营端首页看板
        OperationHomePage homePage = new OperationHomePage();
        //运营团队（当前运营团队状态=启用的数量）
        Integer organizationNum = organizationNum();
        //校区（当前校区状态=启用的数量）
        Integer campusNum = this.campusNum();
        //全职教练（当前教练类型=全职，教练状态=启用的数量）
        List<Coach> coaches = this.coachMapper.selectList(new LambdaQueryWrapper<Coach>()
                .eq(Coach::getStatus, CommonStatusEnum.ENABLE.getValue()));
        Integer fullTimeCoach = coachNum(coaches, CoachTypeEnum.FULL_TIME);
        //兼职教练（当前教练类型=兼职，教练状态=启用的数量）
        Integer partTimeCoach = coachNum(coaches, CoachTypeEnum.PART_TIME);
        //累计学员（学员有过上课记录，不区分学员当前是否有上课）
        Integer grandTotalStuNum = grandTotalStuNum();
        //在学学员（截止当前时间，学员有待开班或开班中的课程）
        Integer studyingStuNum = 0;
        int currentClassNum = 0;
        int finishClassNum = 0;
        int todayScheduleNum = 0;
        Map<String, Integer> addWillMap = new HashMap<>();
        Map<String, Integer> classCount = new HashMap<>();
        Map<String, Long> currentClassMap = new HashMap<>();
        //截止当前时间的班级信息
        List<ClassInfo> classInfos = this.classInfoMapper.selectList(new LambdaQueryWrapper<ClassInfo>()
                .le(BaseEntity::getCreatedDate, date)
                .orderByDesc(BaseEntity::getCreatedDate));
        if (ParamUtils.isNotEmpty(classInfos)) {
            studyingStuNum = this.studyingStuNum(classInfos);
            //当前开设班级(班级状态=开班中)
            List<ClassInfo> currentClassInfos = classInfos(classInfos, ClassInfoStatusEnum.OPEN_CLASS.getValue());
            currentClassNum = currentClassInfos.size();
            //当前待开课班级 (班级状态=待开课)
            List<ClassInfo> finishClassInfo = classInfos(classInfos, Arrays.asList(ClassInfoStatusEnum.FINISH_CLASS.getValue(), ClassInfoStatusEnum.OPEN_CLASS.getValue(), ClassInfoStatusEnum.DISSOLVE_CLASS_EXPIRED.getValue(),
                    ClassInfoStatusEnum.DISSOLVE_CLASS_DEFICIENCY.getValue()));

            if (ParamUtils.isNotEmpty(finishClassInfo)) {
                Map<String, Map<String, List<ClassInfo>>> allClassMap = finishClassInfo.stream()
                        .collect(Collectors.groupingBy(ClassInfo::getOrganizationId, Collectors.groupingBy(BaseEntity::getId)));

                List<String> finishClassIds = finishClassInfo.stream().map(BaseEntity::getId).collect(Collectors.toList());

                List<SpellingClassPeriod> spellingClassPeriods = this.spellingClassPeriodMapper.selectList(new LambdaQueryWrapper<SpellingClassPeriod>()
                        .in(SpellingClassPeriod::getClassInfoId, finishClassIds).eq(SpellingClassPeriod::getCourseDate, currentDate));
                //今日排课数 （已经拼班成功，上课日期=今天的节课数量）
                todayScheduleNum = spellingClassPeriods.size();
                //当前待开课班级
                finishClassNum = finishClassInfo.stream().filter(f -> f.getClassStatus().equals(ClassInfoStatusEnum.FINISH_CLASS.getValue()))
                        .collect(Collectors.toList()).size();

                List<ClassInfo> infos = classInfos.stream()
                        .filter(f -> ParamUtils.isNotEmpty(f.getCompleteTime()) && TimeUtils.toText(f.getCompleteTime(), TimeUtils.DATE_FORMAT)
                                .equals(TimeUtils.toText(currentDate, TimeUtils.DATE_FORMAT))).collect(Collectors.toList());
                if (ParamUtils.isNotEmpty(infos)) {
                    Map<String, Map<String, List<ClassInfo>>> classMap = infos.stream()
                            .collect(Collectors.groupingBy(ClassInfo::getOrganizationId, Collectors.groupingBy(BaseEntity::getId)));
                    List<String> classIds = infos.stream().map(BaseEntity::getId).collect(Collectors.toList());
                    //新增报名人数	今日截止当前时间，本运营团队，参与拼班成功的学员数量。不管学员是否已经有待开课、开班中的课程
                    addWillMap = this.addWillNum(classIds, classMap);
                }
                //上课数（节课）	本运营团队，已经拼班成功，上课日期=今天的节课数量
                classCount = this.classCount(allClassMap, spellingClassPeriods);

            }
            //当前开设班级	截止当前时间，本运营团队，班级状态=开班中
            currentClassMap = this.classInfos(currentClassInfos);
        }
        //今日销课数(上课日期=今天，学员考勤状态=签到的数量。)
        List<CourseScheduleDetail> details = this.courseScheduleDetailMapper.selectList(new LambdaQueryWrapper<CourseScheduleDetail>()
                .eq(CourseScheduleDetail::getCourseDate, currentDate));
        Long todayClassNum = this.todayClassNum(details);
        //今日销课金额 (今日完成考勤，学员签到数对应产生的订单，实付金额累加)
        List<Orders> orders = this.pinOrders(details, currentDate);
        BigDecimal orderActualAmount = this.orderActualAmount(orders);
        //今日充值金额
        BigDecimal totalAmount = this.fundsTradeBill(currentDate);

        homePage.setOrganizationCount(organizationNum)
                .setCampusCount(campusNum)
                .setFullTimeCoachCount(fullTimeCoach)
                .setPartTimeCoachCount(partTimeCoach)
                .setTotalStuCount(grandTotalStuNum)
                .setStudyingStuCount(studyingStuNum)
                .setClassCount(currentClassNum)
                .setWaitClassCount(finishClassNum)
                .setTodayCourseCount(todayScheduleNum)
                .setTodayPinClassCount(todayClassNum.intValue())
                .setTodayPinClassAmount(orderActualAmount)
                .setTodayPayAmount(totalAmount)
                .setStatisticalTime(date);

        this.operationHomePageMapper.delete(null);
        this.operationHomePageMapper.insert(homePage);


        //排课人数 本运营团队，上课日期=今天，每个课表的学员数累加
        Map<String, Integer> scheduleStuCount = new HashMap<>();
        //获取考勤数量
        Map<String, Long> attendanceMap = new HashMap<>();
        List<StudentAttendance> studentAttendances = this.studentAttendanceMapper.selectList(null);
        if (ParamUtils.isNotEmpty(studentAttendances)) {
            attendanceMap = studentAttendances.stream().collect(Collectors.groupingBy(StudentAttendance::getScheduleDetailId, Collectors.counting()));
        }
        //签到人数 本运营团队，上课日期=今天，学员考勤状态=签到人数
        Map<String, Integer> signCount = new HashMap<>();
        //未到人数 上课日期=今天，学员考勤状态=未到/请假人数
        Map<String, Integer> absentStuCount = new HashMap<>();
        //请假人数 上课日期=今天，学员考勤状态=未到/请假人数
        Map<String, Integer> leaveStuCount = new HashMap<>();
        if (ParamUtils.isNotEmpty(details)) {
            Map<String, List<CourseScheduleDetail>> scheduleDetailMap = details.stream().collect(Collectors.groupingBy(CourseScheduleDetail::getCampusId));
            for (String campusId : scheduleDetailMap.keySet()) {
                List<CourseScheduleDetail> list = scheduleDetailMap.get(campusId);
                int totalNum = 0;
                for (CourseScheduleDetail courseScheduleDetail : list) {
                    Long count = attendanceMap.get(courseScheduleDetail.getId());
                    int stuNum = ParamUtils.isEmpty(count) ? 0 : count.intValue();
                    totalNum = totalNum + stuNum;
                }
                int totalActualNum = list.stream().mapToInt(CourseScheduleDetail::getActualNum).sum();
                int totalAbsentNum = list.stream().mapToInt(CourseScheduleDetail::getAbsentNum).sum();
                int totalLeaveNum = list.stream().mapToInt(CourseScheduleDetail::getLeaveNum).sum();
                scheduleStuCount.put(campusId, totalNum);
                signCount.put(campusId, totalActualNum);
                absentStuCount.put(campusId, totalAbsentNum);
                leaveStuCount.put(campusId, totalLeaveNum);

            }

        }
        //销课金额 今日完成考勤，学员签到数对应产生的订单，实付金额累加
        Map<String, BigDecimal> pinAmountMap = new HashMap<>();
        if (ParamUtils.isNotEmpty(orders)) {
            Map<String, List<Orders>> orderMap = orders.stream().collect(Collectors.groupingBy(Orders::getCampusId));
            orderMap.forEach((campusId, orderList) -> {
                BigDecimal totalActualAmount = BigDecimal.valueOf(orderList.stream().mapToDouble(i -> Double.parseDouble(i.getActuallyAmount().toString())).reduce(0, Double::sum));
                pinAmountMap.put(campusId, totalActualAmount);
            });
        }


        List<OrganizationCampus> campuses = this.organizationCampusMapper.selectList(null);
        if (ParamUtils.isNotEmpty(campuses)) {
            this.organizationHomePageMapper.delete(null);
            for (OrganizationCampus campus : campuses) {
                //机构端首页看板
                String campusId = campus.getId();
                String orgId = campus.getOrganizationId();
                Long currentClassCount = currentClassMap.get(campusId);
                OrganizationHomePage page = new OrganizationHomePage();
                page.setCampusId(campusId)
                        .setOrganizationId(campus.getOrganizationId())
                        .setAbsentCount(absentStuCount.get(campusId))
                        .setStatisticalTime(date)
                        .setAddApplyCount(addWillMap.get(orgId))
                        .setArrangeClassCount(scheduleStuCount.get(campusId))
                        .setPinClassAmount(pinAmountMap.get(campusId))
                        .setCurrentClassCount(ParamUtils.isNotEmpty(currentClassCount) ? Integer.parseInt(currentClassCount.toString()) : 0)
                        .setLeaveCount(leaveStuCount.get(campusId))
                        .setSignUpCount(signCount.get(campusId))
                        .setTakeClassCount(classCount.get(orgId));
                this.organizationHomePageMapper.insert(page);
            }
        }
    }

    private Map<String, Integer> classCount(Map<String, Map<String, List<ClassInfo>>> orgClassMap, List<SpellingClassPeriod> spellingClassPeriods) {
        Map<String, Integer> classCount = new HashMap<>();
        if (ParamUtils.isNotEmpty(spellingClassPeriods)) {
            Map<String, List<SpellingClassPeriod>> periodMap = spellingClassPeriods.stream().collect(Collectors.groupingBy(SpellingClassPeriod::getClassInfoId));
            orgClassMap.forEach((orgId, classInfos) -> {
                int num = 0;
                for (String classId : classInfos.keySet()) {
                    List<SpellingClassPeriod> periods = periodMap.get(classId);
                    if (ParamUtils.isNotEmpty(periods)) {
                        num += periods.size();
                    }

                }
                classCount.put(orgId, num);
            });
        }
        return classCount;
    }

    private List<ClassStudent> getClassStudents(List<String> classIds) {
        return this.classStudentMapper.selectList(new LambdaQueryWrapper<ClassStudent>()
                .in(ClassStudent::getClassInfoId, classIds));
    }

    private Map<String, Integer> addWillNum(List<String> classIds, Map<String, Map<String, List<ClassInfo>>> orgClassMap) {

        Map<String, Integer> addWillNum = new HashMap<>();
        List<ClassStudent> classStudents = this.getClassStudents(classIds);
        if (ParamUtils.isNotEmpty(classStudents)) {
            Map<String, List<ClassStudent>> stuMap = classStudents.stream().collect(Collectors.groupingBy(ClassStudent::getClassInfoId));
            orgClassMap.forEach((orgId, info) -> {
                //
                List<String> stuIds = new ArrayList<>();
                info.keySet().forEach(classId -> {
                    List<ClassStudent> students = stuMap.get(classId);
                    if (ParamUtils.isNotEmpty(students)) {
                        stuIds.addAll(students.stream().map(ClassStudent::getStudentId).distinct().collect(Collectors.toList()));
                    }
                });
                addWillNum.put(orgId, stuIds.size());
            });
        }

        return addWillNum;
    }


    private Integer organizationNum() {
        return this.organizationMapper.selectList(new LambdaQueryWrapper<Organization>()
                .eq(Organization::getStatus, CommonStatusEnum.ENABLE.getValue())).size();
    }

    private Integer campusNum() {
        return this.organizationCampusMapper.selectList(new LambdaQueryWrapper<OrganizationCampus>()
                .eq(OrganizationCampus::getStatus, CommonStatusEnum.ENABLE.getValue())).size();
    }

    private Integer coachNum(List<Coach> coaches, CoachTypeEnum partTime) {
        return (int) Optional.ofNullable(coaches)
                .orElse(new ArrayList<>())
                .stream().filter(f -> f.getCoachType().equals(partTime.getValue())).count();
    }

    private Integer grandTotalStuNum() {
        int grandTotalStuNum = 0;
        List<StudentAttendance> studentAttendances = this.studentAttendanceMapper.selectList(new LambdaQueryWrapper<StudentAttendance>()
                .eq(BaseEntity::getDeleted, false));
        if (ParamUtils.isNotEmpty(studentAttendances)) {
            List<String> classStudentIds = studentAttendances.stream().map(StudentAttendance::getClassStudentId)
                    .distinct()
                    .collect(Collectors.toList());
            List<ClassStudent> classStudents = this.classStudentMapper.selectBatchIds(classStudentIds);
            List<String> stuIds = classStudents.stream().map(ClassStudent::getStudentId).distinct().collect(Collectors.toList());
            grandTotalStuNum = stuIds.size();
        }
        return grandTotalStuNum;
    }

    private Integer studyingStuNum(List<ClassInfo> classInfos) {
        List<String> classIds = classInfos.stream()
                .filter(f -> f.getClassStatus().equals(ClassInfoStatusEnum.FINISH_CLASS.getValue()) ||
                        f.getClassStatus().equals(ClassInfoStatusEnum.OPEN_CLASS.getValue()))
                .map(BaseEntity::getId).collect(Collectors.toList());
        int studyingStuNum = 0;
        if (ParamUtils.isNotEmpty(classIds)){
            List<ClassStudent> classStudents = getClassStudents(classIds);
            if (ParamUtils.isNotEmpty(classStudents)) {
                List<String> stuIds = classStudents.stream().map(ClassStudent::getStudentId).distinct().collect(Collectors.toList());
                studyingStuNum = stuIds.size();
            }
        }

        return studyingStuNum;
    }

    private List<ClassInfo> classInfos(List<ClassInfo> classInfos, String value) {
        return classInfos.stream()
                .filter(f -> f.getClassStatus().equals(value))
                .collect(Collectors.toList());
    }


    private List<ClassInfo> classInfos(List<ClassInfo> classInfos, List<String> value) {
        List<ClassInfo> results = new ArrayList<>();
        classInfos.forEach(i -> {
            if (value.contains(i.getClassStatus())) {
                results.add(i);
            }
        });

        return results;
    }


    /***
     * 各校区的开班中的班级数
     * @param classInfos 开班中的班级
     * @return Map<String, Long>
     */
    private Map<String, Long> classInfos(List<ClassInfo> classInfos) {
        Map<String, Long> currentClassMap = new HashMap<>();
        if (ParamUtils.isNotEmpty(classInfos)) {
            currentClassMap = classInfos.stream()
                    .collect(Collectors.groupingBy(ClassInfo::getCampusId, Collectors.counting()));
        }
        return currentClassMap;
    }

    private Long todayClassNum(List<CourseScheduleDetail> details) {
        long sum = 0L;
        if (ParamUtils.isNotEmpty(details)) {
            long actualNum = details.stream().mapToInt(CourseScheduleDetail::getActualNum).summaryStatistics().getSum();
            long absentNum = details.stream().mapToInt(CourseScheduleDetail::getAbsentNum).summaryStatistics().getSum();
            long leaveNum = details.stream().mapToInt(CourseScheduleDetail::getLeaveNum).summaryStatistics().getSum();
            sum = actualNum + absentNum + leaveNum;
        }
        return sum;
    }

    private BigDecimal orderActualAmount(List<Orders> orders) {
        BigDecimal totalActualAmount = BigDecimal.ZERO;
        if (ParamUtils.isNotEmpty(orders)) {
            totalActualAmount = BigDecimal.valueOf(orders.stream().mapToDouble(i -> Double.parseDouble(i.getActuallyAmount().toString())).reduce(0, Double::sum));
        }
        return totalActualAmount;
    }


    public List<Orders> pinOrders(List<CourseScheduleDetail> details, Date currentDate) {
        List<Orders> orders = null;
        if (ParamUtils.isNotEmpty(details)) {
            List<String> scheduleDetailIds = details.stream().map(BaseEntity::getId).collect(Collectors.toList());
            List<StudentAttendance> studentAttendances = this.studentAttendanceMapper
                    .attendanceInfo(scheduleDetailIds, currentDate, AttendanceStatusEnum.NOCHECK.getValue());

            if (ParamUtils.isNotEmpty(studentAttendances)) {
                List<String> orderIds = studentAttendances.stream().map(StudentAttendance::getOrderId).collect(Collectors.toList());
                orders = this.ordersMapper.selectList(new LambdaQueryWrapper<Orders>()
                        .in(BaseEntity::getId, orderIds)
                        .eq(Orders::getOrderStatus, OrderStatusEnum.ALREADY.getValue()));
            }
        }
        return orders;
    }

    private BigDecimal fundsTradeBill(Date currentDate) {
        BigDecimal totalAmount = BigDecimal.ZERO;
        List<FundsTradeBill> fundsTradeBills = this.fundsTradeBillMapper.selectList(new LambdaQueryWrapper<FundsTradeBill>()
                .eq(FundsTradeBill::getTradeType, TradeTypeEnum.RECHARGE.getValue()));
        if (ParamUtils.isNotEmpty(fundsTradeBills)) {
            List<BigDecimal> amounts = fundsTradeBills.stream()
                    .filter(f -> TimeUtils.toDate(TimeUtils.toText(f.getOperateTime(), TimeUtils.DATE_FORMAT), TimeUtils.DATE_FORMAT).equals(currentDate))
                    .map(FundsTradeBill::getAmount)
                    .collect(Collectors.toList());
            if (ParamUtils.isNotEmpty(amounts)) {
                for (BigDecimal amount : amounts) {
                    totalAmount = totalAmount.add(amount);
                }
            }
        }
        return totalAmount;
    }
}
